/**
 * 
 */
package com.cfweb.controller.alert;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpHeaders;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.cfweb.controller.BasicInfo;
import com.cfweb.controller.LoginHandler;
import com.cfweb.dao.CfAppMapper;
import com.cfweb.dao.CfTargetMapper;
import com.cfweb.dao.CfUserMapper;
import com.cfweb.domain.CfApp;
import com.cfweb.domain.CfAppExample;
import com.cfweb.domain.CfTarget;
import com.cfweb.domain.CfTargetExample;
import com.cfweb.domain.CfUser;
import com.cfweb.domain.CfUserExample;
import com.cfweb.exception.CloudFoundryApiException;
import com.cfweb.pojo.OauthToken;
import com.cfweb.util.CloudFoundryClientUtil;
import com.cfweb.util.ExtJSResponse;
import com.cfweb.util.HttpClientResponse;
import com.cfweb.util.HttpClientUtil;
import com.cfweb.util.HttpStatusUtil;
import com.cfweb.util.JsonUtil;
import com.cfweb.util.MailUtil;

/**
 * @Title: AlertManagement.java
 * @Package com.cfweb.controller.alert
 * @Description: 进行监控预警
 * @author 龙立强
 * @date 2016年1月13日 下午8:58:58
 * @version V1.0
 */

@Service
@Path("/alert")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AlertManagement {

	private String start;
	private String from;
	private String password;
	private String to;

	@Value("${start}")
	public void setStart(String start) {
		this.start = start;
	}

	@Value("${from}")
	public void setFrom(String from) {
		this.from = from;
	}

	@Value("${password}")
	public void setPassword(String password) {
		this.password = password;
	}

	@Value("${to}")
	public void setTo(String to) {
		this.to = to;
	}

	Logger logger = Logger.getLogger(BasicInfo.class);

	@Autowired
	private HttpSession session;

	@Autowired
	private CfAppMapper caMapper;

	@Autowired
	private CfTargetMapper targetMapper;

	@Autowired
	private CfUserMapper userMapper;

	private final int APPSTATE = 3;

	/***
	 * 监控预警 预警规则：遍历所有应用，遍历应用的所有实例
	 * 
	 * @return
	 * @throws Exception
	 */
	@Path("/appMonitAlert")
	@GET
	public ExtJSResponse appMonitAlert() throws Exception {

		List<Map<String, String>> returnList = new ArrayList<Map<String, String>>(); // 最终返回值
		// 首先查询cf_app，遍历所有运行中的应用
		List<CfApp> apps = new ArrayList<CfApp>();
		CfAppExample caExample = new CfAppExample();
		caExample.createCriteria().andStateEqualTo(this.APPSTATE);
		apps = caMapper.selectByExample(caExample);

		if (apps.size() != 0) {// 能查到应用才会进行循环

			// 循环遍历每一个APP来监控其状态
			for (CfApp app : apps) {

				// Map<String, Object> data = null;
				try {
					String host = ((URL) session.getAttribute(LoginHandler.TARGET)).toString();
					String urlPath = host + "/v2/apps/" + app.getUuid() + "/stats";
					String content = CloudFoundryClientUtil.doCCGet(urlPath, null, session);
					Map<String, Object> stats = JsonUtil.convertJsonToMap(content);

					int count = 0;

					double mem = 0;
					double disk = 0;
					for (int i = 0; i < stats.size(); i++) { // 循环一个App的所有实例

						Map<String, Object> instance = (Map<String, Object>) ((Map<String, Object>) stats
								.get(String.valueOf(i))).get("stats");

						if (instance == null) {

							continue;// 如果没有实例，跳出当次循环
						}

						// 开始进行判断，如果当前使用值超过预定限额就会预警

						double mem_quota = Double.valueOf(String.valueOf(instance.get("mem_quota")));// 内存限额
						double disk_quota = Double.valueOf(String.valueOf(instance.get("disk_quota")));// 硬盘限额

						Map<String, Object> usage = (Map<String, Object>) instance.get("usage");
						mem = Double.valueOf(String.valueOf(usage.get("mem")));// 当前内存使用量
						disk = Double.valueOf(String.valueOf(usage.get("disk")));// 当前硬盘使用量

						double limit = 0.8;// 测试值，从配置文件中读取
						if (mem / mem_quota >= limit || disk / disk_quota >= limit) {// 超过限定，提出预警

							count++;
						}
					}
					// if(count == stats.size())
					{// 说明每个实例都已经超过阈值

						// 构造返回值{appName: 'app1', orgName: 'org1', memory:
						// '512M', cpu: '80%', totMemory: '1G'},
						Map<String, String> data = new HashMap<String, String>();
						data.put("appName", app.getAppName());
						data.put("orgName", app.getOrg());
						data.put("instanceCount", String.valueOf(stats.size()));
						data.put("memory", String.valueOf(mem));

						data.put("disk", String.valueOf(disk));

						returnList.add(data);
					}

				} catch (Exception e) {
					if (e instanceof CloudFoundryApiException && ((CloudFoundryApiException) e).getErrorCode() == 400) {
						return null;
					} else {
						throw e;
					}
				}

			} // for(CfApp app:apps)

		}

		return ExtJSResponse.successResWithData(returnList);

	}

	/***
	 * 监控预警 预警规则：根据定时器自动遍历所有应用，遍历应用的所有实例
	 * 
	 * @return
	 * @throws Exception
	 */
	@Path("/autoAppMonitAlert")
	@GET
	public void autoAppMonitAlert() throws Exception {

		// //首先读取配置文件timer.properties，来判断是否需要进行自动监控
		// Properties prop = new Properties();
		// InputStream in = getClass().getResourceAsStream("timer.properties");
		// prop.load(in);
		// String flag = prop.getProperty("start").toString();

		if (start.equals("yes")) {// 代表自动监控

		} else {
			return;// 不要进行预警，直接退出
		}

		// 从cf_target表中获取target
		CfTargetExample targetExample = new CfTargetExample();
		targetExample.createCriteria();
		CfTarget cfTarget = targetMapper.selectByExample(targetExample).get(0);
		String host = cfTarget.getTarget().toString().trim();
		// String host = prop.getProperty("target").toString();
		// //http://api.yjs.cf
		// String mail = prop.getProperty("mail").toString();

		List<Map<String, String>> returnList = new ArrayList<Map<String, String>>(); // 最终返回值
		// 首先查询cf_app，遍历所有运行中的应用
		List<CfApp> apps = new ArrayList<CfApp>();
		CfAppExample caExample = new CfAppExample();
		caExample.createCriteria().andStateEqualTo(this.APPSTATE);
		apps = caMapper.selectByExample(caExample);

		if (apps.size() != 0) {// 能查到应用才会进行循环

			// 循环遍历每一个APP来监控其状态
			for (CfApp app : apps) {

				try {

					// (1)需要与uaa进行认证，不通过session，直接发送rest请求给cf集群
					Map<String, String> headers1 = new HashMap<String, String>();// 构建请求头hearder
					// 获取admin用户的密码,管理员只有一个admin，故此处写死
					CfUserExample example = new CfUserExample();
					example.createCriteria().andUserNameEqualTo("admin");
					List<CfUser> cfuserList = userMapper.selectByExample(example);
					if (cfuserList == null || cfuserList.size() < 1) {
						return;
					}

					CfUser cfuser = cfuserList.get(0);
					// 通过用户名和密码开始进行认证
					Map<String, String> params = new LinkedHashMap<String, String>();
					params.put("grant_type", "password");
					params.put("username", cfuser.getUserName());
					params.put("password", cfuser.getUserPass());

					String uaaHost = host.replaceFirst("api", "uaa");// http://uaa.yjs.cf
					String url = uaaHost + "/oauth/token";// http://uaa.yjs.cf/oauth/token
					// String
					// response=CloudFoundryClientUtil.doOauth(uaaHost+"/oauth/token",
					// params,session);
					// 重新构造doOauth()方法，目的是在没有session的情况下通过admin和密码获取token
					Base64 base64 = new Base64();
					// 目的是将http://api.yjs.cf变成uaa.yjs.cf
					uaaHost = host.replaceFirst(host.substring(0, host.indexOf(".")), "uaa");

					headers1.put(HttpHeaders.HOST, uaaHost);
					// headers1.put(HttpHeaders.HOST,"uaa.yjs.cf");
					headers1.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
					headers1.put("Authorization", String.format("Basic %s",
							new String(base64.encode(String.format("%s:%s", "cf", "").getBytes("UTF-8")), "UTF-8")));

					HttpClientResponse response = HttpClientUtil.doPost(url, params, headers1, false);
					if (!HttpStatusUtil.isSuccessStatus(response.getStatus())) {
						String content = response.getBody();
						Map<String, Object> data = JsonUtil.convertJsonToMap(content);
						throw new CloudFoundryApiException(Integer.valueOf(response.getStatus()),
								(String) data.get("error_description"));
					}
					String responseStr = response.getBody();

					Map<String, Object> data = (Map<String, Object>) JsonUtil.convertJsonToMap(responseStr);
					OauthToken token = new OauthToken((String) data.get("access_token"),
							(String) data.get("token_type"), (String) data.get("refresh_token"),
							(int) data.get("expires_in"));

					// (2)将token放到请求头，重新发起请求
					// 封装请求头
					Map<String, String> headers2 = new HashMap<String, String>();
					headers2.put(HttpHeaders.AUTHORIZATION, token.getTokenType() + " " + token.getAccessToken());
					headers2.put(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
					headers2.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);

					String urlPath = host + "/v2/apps/" + app.getUuid() + "/stats";
					// 自己重新发起请求
					HttpClientResponse response2 = HttpClientUtil.doGet(urlPath, params, headers2, false);
					if (!HttpStatusUtil.isSuccessStatus(response2.getStatus())) {
						String content = response.getBody();
						Map<String, Object> data2 = JsonUtil.convertJsonToMap(content);
						if (data2.get("error") != null) {
							throw new CloudFoundryApiException(Integer.valueOf(response.getStatus()),
									(String) data2.get("error"));
						}
						throw new CloudFoundryApiException(Integer.valueOf(response.getStatus()),
								(String) data2.get("description"));
					}
					String content = response2.getBody();

					// （3）开始遍历返回值
					Map<String, Object> stats = JsonUtil.convertJsonToMap(content);

					int count = 0;
					double mem = 0;
					double disk = 0;
					for (int i = 0; i < stats.size(); i++) { // 循环一个App的所有实例

						Map<String, Object> instance = (Map<String, Object>) ((Map<String, Object>) stats
								.get(String.valueOf(i))).get("stats");

						if (instance == null) {

							continue;// 如果没有实例，跳出当次循环
						}

						// 开始进行判断，如果当前使用值超过预定限额就会预警

						double mem_quota = Double.valueOf(String.valueOf(instance.get("mem_quota")));// 内存限额
						double disk_quota = Double.valueOf(String.valueOf(instance.get("disk_quota")));// 硬盘限额

						Map<String, Object> usage = (Map<String, Object>) instance.get("usage");
						mem = Double.valueOf(String.valueOf(usage.get("mem")));// 当前内存使用量
						disk = Double.valueOf(String.valueOf(usage.get("disk")));// 当前硬盘使用量

						double limit = 0.8;// 测试值
						if (mem / mem_quota >= limit || disk / disk_quota >= limit) {// 超过限定，提出预警

							count++;
						}
					}
					if (count == stats.size()) {// 说明每个实例都已经超过阈值

						Map<String, String> appData = new HashMap<String, String>();
						appData.put("appname", app.getAppName());
						appData.put("instanceCount", String.valueOf(stats.size()));
						appData.put("memory", String.valueOf(mem));
						appData.put("disk", String.valueOf(disk));

						returnList.add(appData);
					}

				} catch (Exception e) {
					if (e instanceof CloudFoundryApiException && ((CloudFoundryApiException) e).getErrorCode() == 400) {

					} else {
						throw e;
					}
				}

			} // for(CfApp app:apps)

			// 所有循环结束，发送预警邮件
			String htmlText = " 管理员您好：\r" + "以下应用中实例占用资源已经超过阈值，提出预警";
			for (Map<String, String> map : returnList) {

				htmlText = htmlText + "\r应用名称：" + map.get("appname") + "  实例个数：" + map.get("instanceCount") + "\r";
			}

			// 获取 邮箱
			if (returnList.size() != 0) {

				MailUtil.send(htmlText, "应用预警", from, password, to);
			}

		}

	}

}
